Normal 벡터를 저장하는 GBuffer 포맷

1. 문제


아래와 같이 Normal이 이상하게 저장되어 라이팅 계산이 제대로 되지 않는 문제가 있었다.

Normal 이상
4. Archive/WhiteEngine/__Attachments/Pasted image 20251017203455.png
정상
4. Archive/WhiteEngine/__Attachments/Pasted image 20251017203524.png

원인으로 GBuffer중 Normal을 저장하는 버퍼를 기존의 백버퍼에서 사용하는 포맷인 DXGI_FORMAT_R8G8B8A8_UNORM을 사용하였더니, -1~1의 값을 가지는 Normal이 0~1의 값으로 클램핑되어 일어난 일이였다.

2. 시도


방법 1.
-1~1값을 가지는 Normal을 0~1값으로 인코딩하여 저장하는 것이다. 반대로 사용할 때 다시 -1~1값으로 디코딩을 해주어야 한다.

// 벡터를 normalize하고 0 ~ 1값으로 인코딩
float3 EncodeVector(float3 Vector)
{
    return (normalize(Vector) + 1.0f) / 2.0f;
}

// 0 ~ 1로 인코딩된 벡터를 -1 ~ 1로 디코딩
float3 DecodeVector(float3 EncodedVector)
{
    return EncodedVector * 2 - 1.0f;
}
float3 EncodedNormalW = EncodeVector(NormalW);
GBufferA = float4(EncodedNormalW, 1.0f);
float3 WorldNormal = gTexture[gGBufferATextureIndex].Sample(gsamLinearWrap, TexC).rgb;
WorldNormal = DecodeVector(WorldNormal);

4. Archive/WhiteEngine/Attachments/Pasted image 20250410122213.png
라이팅 계산 자체는 되었지만, Specular부분에 물결무늬가 발생한 것을 알 수 있다. 이것은 Normal이 각 채널 8비트의 낮은 정밀도에 인코딩, 디코딩되면서 발생한 값 손실 때문이다.

방법 2.
DXGI_FORMAT_R16G16B16A16_FLOAT 포맷을 사용하여 Normal을 저장해 준다.
4. Archive/WhiteEngine/Attachments/Pasted image 20250410135912.png
정상적으로 라이팅 계산이 되었음을 알 수 있다.

3. 결과